#include <freertos/FreeRTOS.h>
#include <freertos/task.h>
#include <esp_timer.h>
#include "keyBoard.h"
#include "tm1638.h"

// 键盘事件队列
static QueueHandle_t evt_q;

// 读键盘任务
// param是指向TM1638对象的指针
void keyTask(void *param)
{
    // extern QueueHandle_t evt_q;
    TM1638 *myTM1638 = (TM1638 *)param;
    static uint8_t key_state[KEY_NUM]; // 按键状态：0=松 1=短按 2=长按
    static uint16_t key_cnt[KEY_NUM];  // 按键次数计数器
    keyBuf_t keybuf;                   // 键盘缓冲区
    uint32_t raw;                      // 键盘缓冲区组合成的真值
    while (1)
    {
        raw = myTM1638->readKey(&keybuf);

        for (int i = 0; i < KEY_NUM; ++i)
        {
            bool down = raw & (1U << i);   // 测试第i位的按键是否被按下。
            if (down && key_state[i] == 0) // 刚被按下，即检测到上升沿。
            {
                key_state[i] = 1;
                key_cnt[i] = 0;
                key_event_t ev = {.key_id = (uint8_t)i, .type = KEY_EV_PRESS};
                xQueueSend(evt_q, &ev, 0);
            }
            else if (!down && key_state[i]) // 刚被释放，也即检测到下降沿.
            {
                key_event_t ev = {.key_id = (uint8_t)i, .type = KEY_EV_RELEASE};
                xQueueSend(evt_q, &ev, 0);
                key_state[i] = 0;
            }
            else if (down && key_state[i]) // 连续检测到按下。
            {
                key_cnt[i]++;
                if (key_cnt[i] == LONG_TICKS) // 长按
                {
                    key_state[i] = 2;
                    key_event_t ev = {.key_id = (uint8_t)i, .type = KEY_EV_LONG};
                    xQueueSend(evt_q, &ev, 0);
                }
                else if (key_cnt[i] > LONG_TICKS &&
                         (key_cnt[i] - LONG_TICKS) % REPEAT_TICKS == 0) // 多次重复长按。
                {
                    key_event_t ev = {.key_id = (uint8_t)i, .type = KEY_EV_REPEAT};
                    xQueueSend(evt_q, &ev, 0);
                }
            }
        }
        vTaskDelay(pdMS_TO_TICKS(SCAN_PERIOD_MS));
    }
}

KeyBoard::KeyBoard(TM1638 &tm) : m_tm1638(tm) {}

void KeyBoard::scan()
{
    uint32_t now = esp_timer_get_time() / 1000;
    m_keyRaw = m_tm1638.readKey();
    // 沿检测
    if (m_keyRaw != m_lastRaw)
    {
        m_lastRaw = m_keyRaw;
        m_debounceStart = now;
        return;
    }

    uint32_t oldStable = m_stableRaw;

    // 如果按键没有变化，则进一步处理消抖
    if (now - m_debounceStart >= DEBOUNCE_MS)
    {
        // 检测到一次新的稳定值
        if (m_keyRaw != m_stableRaw)
        {
            uint32_t oldStable = m_stableRaw;
            m_stableRaw = m_keyRaw;

            // 检测按下和釋放
            uint32_t pressed = m_stableRaw & ~oldStable;
            uint32_t released = ~m_stableRaw & oldStable;

            // if (cb)
            //     cb(m_stableRaw, pressed, released);
        }
    }

    // 检测按键沿信号
    uint32_t raisePluse = m_stableRaw & ~oldStable;
    uint32_t fallPulse = ~m_stableRaw & oldStable;
    uint32_t pressed = m_stableRaw & oldStable;
    static uint32_t pressTime[KEY_NUM] = {0};
    // static uint32_t longPressed = 0;

    // 产生键盘事件队列
    for (int i = 0; i < KEY_NUM; ++i)
    {
        if (raisePluse & 1UL << i) // 刚被按下，即检测到上升沿。
        {
            // 记录按下的时间戳，提供长按判断.
            pressTime[i] = now;

            // 如果队列未满，则把事件入队
            uint8_t next = (m_keyWi + 1) % KEY_BUF_SIZE;
            if (next != m_keyRi)
            {
                m_keyFifo[m_keyWi].key_id = i;
                m_keyFifo[m_keyWi].type = KEY_EV_PRESS;
                m_keyFifo[m_keyWi].timeStamp = now;
                m_keyWi = next;
            }
        }

        if (fallPulse & 1UL << i) // 刚被释放，也即检测到下降沿.
        {
            // 复位长按状态。
            // longPressed &= ~(1UL << i);

            // 如果队列未满，则把事件入队
            uint8_t next = (m_keyWi + 1) % KEY_BUF_SIZE;
            if (next != m_keyRi)
            {
                m_keyFifo[m_keyWi].key_id = i;
                m_keyFifo[m_keyWi].type = KEY_EV_RELEASE;
                m_keyFifo[m_keyWi].timeStamp = now;
                m_keyWi = next;
            }
        }        

        // if ((pressed & 1UL << i) && (now - pressTime[i] >= LONG_PRESS) && (longPressed & 1UL << i) == 0UL) // 连续检测到按下。
        if ((pressed & 1UL << i) && (now - pressTime[i] >= LONG_PRESS)) // 连续检测到按下。
        {
            // 记录长按状态，避免重复检测长按。
            // longPressed |= 1UL << i;

            // 如果队列未满，则把事件入队
            uint8_t next = (m_keyWi + 1) % KEY_BUF_SIZE;
            if (next != m_keyRi)
            {
                m_keyFifo[m_keyWi].key_id = i;
                m_keyFifo[m_keyWi].type = KEY_EV_LONG;
                m_keyFifo[m_keyWi].timeStamp = now;
                m_keyWi = next;
            }
        }

        if (cb)
        {
            key_event_t ev;
            if (getKeyEvent(&ev))
            {
                cb(ev);
            }
        }
    }
}

bool KeyBoard::getKeyEvent(key_event_t *ev)
{
    if (ev == nullptr or m_keyRi == m_keyWi)
        return false;

    // if (m_keyRi != m_keyWi) // 可以删除判断
    {
        // ESP_LOGI("ev", "key_ri= %d, key_wi= %d", key_ri, key_wi);
        ev->key_id = m_keyFifo[m_keyRi].key_id;
        ev->type = m_keyFifo[m_keyRi].type;
        ev->timeStamp = m_keyFifo[m_keyRi].timeStamp;
        m_keyRi = (m_keyRi + 1) % KEY_BUF_SIZE;
        return true;
    }
}
